package cn.elwy.rcp.ui.explorer.core.util;

import java.io.File;
import java.util.Collection;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNCommitClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatus;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import cn.elwy.common.exception.RunException;
import cn.elwy.common.util.AssertUtil;

/**
 * SVNKit 封装工具类
 * @author huangsq
 * @version 1.0, 2014-06-16
 * @since 1.0, 2014-06-16
 */
public class SvnUtil {

	private static Logger logger = LoggerFactory.getLogger(SvnUtil.class);

	public static final String SVN_BASE_DIRS[] = { "trunk", "branches", "tags" };

	static {
		// 设置通过不同的协议初始化版本库
		FSRepositoryFactory.setup();
		DAVRepositoryFactory.setup();
		SVNRepositoryFactoryImpl.setup();
	}

	/**
	 * 获取SVN客户端管理对象
	 * @param url SVN的URL
	 * @param username 用户名
	 * @param password 密码
	 * @return
	 * @throws SVNException
	 */
	public static SVNClient getClientManager(String url, String username, String password) throws SVNException {
		return getClientManager(getSVNURL(url), username, password);
	}

	/**
	 * 获取SVN客户端管理对象
	 * @param svnURL SVN的URL
	 * @param username 用户名
	 * @param password 密码
	 * @return
	 * @throws SVNException
	 */
	private static SVNClient getClientManager(SVNURL svnURL, String username, String password) throws SVNException {
		SVNRepository repository = SVNRepositoryFactory.create(svnURL);
		// 创建身份验证管理器
		ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
		// 设置身份验证管理器
		repository.setAuthenticationManager(authManager);
		SVNNodeKind nodeKind = repository.checkPath("", -1);
		if (nodeKind == SVNNodeKind.NONE) {
			throw new RunException("'" + svnURL.getPath() + "' is not exists");
		}
		// 创建默认选项
		DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
		SVNClientManager clientManager = SVNClientManager.newInstance(options, authManager);
		SVNClient svnClient = new SVNClient(username, password, svnURL, repository, clientManager);
		return svnClient;
	}

	/**
	 * 转换成SVN的URL对象
	 * @param url svn的URL
	 * @return
	 * @throws SVNException
	 */
	public static SVNURL getSVNURL(String url) throws SVNException {
		return SVNURL.parseURIEncoded(url);
	}

	/**
	 * 检出SVN URL的全部内容到本地
	 * @param clientManager SVN客户端管理对象
	 * @param url svn地址
	 * @param destFile 检出的位置
	 * @return
	 * @throws SVNException
	 */
	public static long checkout(SVNClientManager clientManager, String url, File destFile) throws SVNException {
		return checkout(clientManager, getSVNURL(url), destFile, SVNRevision.HEAD, SVNDepth.INFINITY);
	}

	/**
	 * 检出SVN URL的全部内容到本地
	 * @param clientManager SVN客户端管理对象
	 * @param url svn地址
	 * @param destFile 检出的位置
	 * @return
	 * @throws SVNException
	 */
	public static long checkout(SVNClientManager clientManager, SVNURL url, File destFile) throws SVNException {
		return checkout(clientManager, url, destFile, SVNRevision.HEAD, SVNDepth.INFINITY);
	}

	/**
	 * 检出SVN URL的内容到本地
	 * @param clientManager SVN客户端管理对象
	 * @param url svn地址
	 * @param destFile 检出的位置
	 * @param vision 检出的版本
	 * @param depth 检出的深度，目录、子目录、文件
	 * @return
	 * @throws SVNException
	 */
	public static long checkout(SVNClientManager clientManager, String url, File destPath, SVNRevision vision,
			SVNDepth depth) throws SVNException {
		boolean ignoreExternals = false;
		return checkout(clientManager, getSVNURL(url), destPath, vision, depth, ignoreExternals);
	}

	/**
	 * 检出SVN URL的内容到本地
	 * @param clientManager SVN客户端管理对象
	 * @param url svn地址
	 * @param destFile 检出的位置
	 * @param vision 检出的版本
	 * @param depth 检出的深度，目录、子目录、文件
	 * @return
	 * @throws SVNException
	 */
	public static long checkout(SVNClientManager clientManager, SVNURL url, File destPath, SVNRevision vision,
			SVNDepth depth) throws SVNException {
		boolean ignoreExternals = false;
		return checkout(clientManager, url, destPath, vision, depth, ignoreExternals);
	}

	/**
	 * 检出SVN URL的内容到本地
	 * @param clientManager SVN客户端管理对象
	 * @param url svn地址
	 * @param destFile 检出的位置
	 * @param HEAD 检出的版本
	 * @param depth 检出的深度，目录、子目录、文件
	 * @param ignoreExternals sets externals not to be ignored during the checkout
	 * @return
	 * @throws SVNException
	 */
	private static long checkout(SVNClientManager clientManager, SVNURL url, File destPath, SVNRevision revision,
			SVNDepth depth, boolean ignoreExternals) throws SVNException {
		SVNUpdateClient updateClient = clientManager.getUpdateClient();
		// sets externals not to be ignored during the checkout
		updateClient.setIgnoreExternals(ignoreExternals);
		// returns the number of the HEAD at which the working copy is
		return updateClient.doCheckout(url, destPath, revision, revision, depth, ignoreExternals);
	}

	/**
	 * 清理本地工作副本
	 * @param clientManager SVN客户端管理对象
	 * @param path 本地工作副本
	 * @return
	 * @throws SVNException
	 */
	public static void cleanup(SVNClientManager clientManager, File path) throws SVNException {
		clientManager.getWCClient().doCleanup(path);
	}

	/**
	 * 添加到SVN本地版本控制中
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @throws SVNException
	 */
	public static void addEntry(SVNClientManager clientManager, File localPath) throws SVNException {
		addEntry(clientManager, new File[] { localPath });
	}

	/**
	 * 添加到SVN本地版本控制中
	 * @param clientManager SVN客户端管理对象
	 * @param localPaths 一组工作副本的路径
	 * @throws SVNException
	 */
	private static void addEntry(SVNClientManager clientManager, File[] localPaths) throws SVNException {
		clientManager.getWCClient().doAdd(localPaths, true, false, false, SVNDepth.INFINITY, false, false, true);
	}

	/**
	 * 在SVN服务器上创建一个目录
	 * @param clientManager SVN客户端管理对象
	 * @param url svn的路径
	 * @param message 提交的日志信息
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo makeDirectory(SVNClientManager clientManager, SVNURL url, String message)
			throws SVNException {
		return makeDirectory(clientManager, new SVNURL[] { url }, message);
	}

	/**
	 * 在SVN服务器上创建一个目录
	 * @param clientManager SVN客户端管理对象
	 * @param svnURLs 一组svn的路径
	 * @param message 提交的日志信息
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo makeDirectory(SVNClientManager clientManager, SVNURL[] svnURLs, String message)
			throws SVNException {
		return clientManager.getCommitClient().doMkDir(svnURLs, message);
	}

	/**
	 * 导入一个文件或目录到SVN服务器
	 * @param clientManager SVN客户端管理对象
	 * @param svnURL svn的路径
	 * @param localPath 文件或目录
	 * @param message 提交的日志信息
	 * @param isRecursive 是否递归
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo importFile(SVNClientManager clientManager, SVNURL svnURL, File localPath, String message,
			boolean isRecursive) throws SVNException {
		SVNCommitClient commitClient = clientManager.getCommitClient();
		return commitClient.doImport(localPath, svnURL, message, null, true, true, SVNDepth.fromRecurse(isRecursive));
	}

	/**
	 * 获取一个本地副本的版本号
	 * @param clientManager SVN客户端管理对象
	 * @param file 工作副本的路径
	 * @return
	 * @throws SVNException
	 */
	public static long getRevision(SVNClientManager clientManager, File file) throws SVNException {
		return clientManager.getStatusClient().doStatus(file, true).getRevision().getNumber();
	}

	public static long getRevision(SVNRepository repository, String path) throws SVNException {
		// 获取版本库的path目录
		SVNDirEntry entry = repository.getDir(path, -1, false, (Collection<?>) null);
		long revision = entry.getRevision();
		return revision;
		// 获取版本库的path目录下的所有条目。参数－1表示是最新版本。
		// Iterator<?> iterator = entries.iterator();
		// SVNDirEntry entry = (SVNDirEntry) iterator.next();
		// while (iterator.hasNext()) {
		// System.out.println("/" + (path.equals("") ? "" : path + "/")
		// + entry.getName() + " (author: '" + entry.getAuthor()
		// + "'; revision: " + revision + "; date: " + entry.getDate() + ")");
		// /*
		// * 检查此条目是否为目录，如果为目录递归执行
		// */
		// if (entry.getKind() == SVNNodeKind.DIR) {
		// listEntries(repository, (path.equals("")) ? entry.getName()
		// : path + "/" + entry.getName());
		//
		// }
		//
		// }
	}

	/**
	 * 获取一个副本的状态信息
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @param remote true to check up the status of the item in the repository, that will tell if the local item is
	 *          out-of-date (like '-u' option in the SVN client's 'svn status' command), otherwise false
	 * @return
	 * @throws SVNException
	 */
	public static SVNStatus getStatus(SVNClientManager clientManager, File localPath, boolean remote)
			throws SVNException {
		return clientManager.getStatusClient().doStatus(localPath, remote);
	}

	/**
	 * 提交副本到SVN
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @param message 提交的日志信息
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo commit(SVNClientManager clientManager, File localPath, String message)
			throws SVNException {
		return commit(clientManager, new File[] { localPath }, message, false);
	}

	/**
	 * 提交副本到SVN
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @param message 提交的日志信息
	 * @param keepLocks 保持锁
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo commit(SVNClientManager clientManager, File localPath, String message, boolean keepLocks)
			throws SVNException {
		return commit(clientManager, new File[] { localPath }, message, keepLocks);
	}

	/**
	 * 提交副本到SVN
	 * @param clientManager SVN客户端管理对象
	 * @param localPaths 一组工作副本的路径
	 * @param message 提交的日志信息
	 * @param keepLocks 保持锁
	 * @return
	 * @throws SVNException
	 */
	public static SVNCommitInfo commit(SVNClientManager clientManager, File[] localPaths, String message,
			boolean keepLocks) throws SVNException {
		SVNCommitClient commitClient = clientManager.getCommitClient();
		return commitClient.doCommit(localPaths, keepLocks, message, null, null, false, false, SVNDepth.INFINITY);
	}

	/**
	 * 更新工作副本到最新的版本
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @return
	 * @throws SVNException
	 */
	public static long update(SVNClientManager clientManager, File localPath) throws SVNException {
		return update(clientManager, localPath, SVNRevision.HEAD, SVNDepth.INFINITY);
	}

	/**
	 * 更新工作副本到指定的版本
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @param HEAD 更新到版本
	 * @param depth 更新的深度，目录、子目录、文件
	 * @param ignoreExternals 忽略外部
	 * @return
	 * @throws SVNException
	 */
	public static long update(SVNClientManager clientManager, File localPath, SVNRevision revision, SVNDepth depth)
			throws SVNException {
		return update(clientManager, localPath, revision, depth, false);
	}

	/**
	 * 更新工作副本到指定的版本
	 * @param clientManager SVN客户端管理对象
	 * @param localPath 工作副本的路径
	 * @param HEAD 更新到版本
	 * @param depth 更新的深度，目录、子目录、文件
	 * @param ignoreExternals 忽略外部
	 * @return
	 * @throws SVNException
	 */
	private static long update(SVNClientManager clientManager, File localPath, SVNRevision revision, SVNDepth depth,
			boolean ignoreExternals) throws SVNException {
		SVNUpdateClient updateClient = clientManager.getUpdateClient();
		// sets externals not to be ignored during the update
		updateClient.setIgnoreExternals(ignoreExternals);
		// returns the number of the HEAD wcPath was updated to
		return updateClient.doUpdate(localPath, revision, depth, ignoreExternals, ignoreExternals);
	}

	/**
	 * 判断一个文件或目录是否是一个工作副本
	 * @param localPath 本地路径
	 * @return
	 */
	public static boolean isWorkingCopy(File localPath) {
		if (!localPath.exists()) {
			logger.warn("'" + localPath + "' not exist!");
			return false;
		}
		try {
			if (null == SVNWCUtil.getWorkingCopyRoot(localPath, false)) {
				return false;
			}
		} catch (SVNException e) {
			logger.error(e.getMessage(), e);
		}
		return true;
	}

	/**
	 * 验证一个URL在SVN上是否存在
	 * @param url svn的URL
	 * @return
	 */
	public static boolean isURLExist(String url, String username, String password) {
		try {
			return isURLExist(getSVNURL(url), username, password);
		} catch (SVNException e) {
			logger.warn(e.getMessage(), e);
			return false;
		}
	}

	/**
	 * 验证一个URL在SVN上是否存在
	 * @param url svn的URL
	 * @return
	 */
	public static boolean isURLExist(SVNURL url, String username, String password) {
		try {
			SVNRepository repository = SVNRepositoryFactory.create(url);
			ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
			repository.setAuthenticationManager(authManager);
			SVNNodeKind nodeKind = repository.checkPath("", -1);
			return nodeKind != SVNNodeKind.NONE;
		} catch (SVNException e) {
			logger.warn(e.getMessage(), e);
		}
		return false;
	}

	/**
	 * 获取SVN版本号
	 * @param version 版本字符串
	 * @return
	 */
	public static SVNRevision isHeadVision(String version) {
		if (AssertUtil.isEmpty(version) || version.equals("HEAD")) {
			return SVNRevision.HEAD;
		}
		try {
			long revisionNumber = Long.parseLong(version);
			return SVNRevision.create(revisionNumber);
		} catch (Exception e) {
			if ("WORKING".equals(version)) {
				return SVNRevision.WORKING;
			} else if ("PREV".equals(version)) {
				return SVNRevision.PREVIOUS;
			} else if ("BASE".equals(version)) {
				return SVNRevision.BASE;
			} else if ("COMMITTED".equals(version)) {
				return SVNRevision.COMMITTED;
			} else {
				return SVNRevision.HEAD;
			}
		}
	}

	/**
	 * 获取SVN版本号
	 * @param version 版本字符串
	 * @return
	 */
	public static SVNRevision getSVNRevision(String version) {
		if (AssertUtil.isEmpty(version) || version.equals("HEAD")) {
			return SVNRevision.HEAD;
		}
		try {
			long revisionNumber = Long.parseLong(version);
			return SVNRevision.create(revisionNumber);
		} catch (Exception e) {
			if ("WORKING".equals(version)) {
				return SVNRevision.WORKING;
			} else if ("PREV".equals(version)) {
				return SVNRevision.PREVIOUS;
			} else if ("BASE".equals(version)) {
				return SVNRevision.BASE;
			} else if ("COMMITTED".equals(version)) {
				return SVNRevision.COMMITTED;
			} else {
				return SVNRevision.HEAD;
			}
		}
	}

	/**
	 * 获取仓库根地址
	 * @param repoPath
	 * @return
	 */
	public static String getProjectRoot(String repoPath) {
		for (int i = 0; i < SVN_BASE_DIRS.length; i++) {
			String base = "/" + SVN_BASE_DIRS[i];
			int pos = repoPath.lastIndexOf(base + "/");
			if (repoPath.endsWith(base))
				return repoPath.substring(0, repoPath.length() - base.length());
			if (pos >= 0)
				return repoPath.substring(0, pos);
		}
		return repoPath;
	}

}